#ifndef SYSTEM_HPP
#define SYSTEM_HPP

#include <stdint.h>

typedef struct ADCRegister{
    volatile uint32_t SR;
    volatile uint32_t CR1;
    volatile uint32_t CR2;
    volatile uint32_t SMPR1;
    volatile uint32_t SMPR2;
    volatile uint32_t JOFR[4];
    volatile uint32_t HTR;
    volatile uint32_t LTR;
    volatile uint32_t SQR1;
    volatile uint32_t SQR2;
    volatile uint32_t SQR3;
    volatile uint32_t JSQR;
    volatile uint32_t JDR[4];
    volatile uint32_t DR;

}__attribute__((packed)) ADCRegister_t;

typedef struct CommonADCRegister{
    volatile uint32_t CCR;
}__attribute__((packed)) CommonADCRegister_t;

typedef struct RCCRegister{
    volatile uint32_t CR;
    volatile uint32_t PLLCFGR;
    volatile uint32_t CFGR;
    volatile uint32_t CIR;
    volatile uint32_t AHB1RSTR;
    volatile uint32_t AHB2RSTR;
    volatile uint32_t Reserved1[2];
    volatile uint32_t APB1RSTR;
    volatile uint32_t APB2RSTR;
    volatile uint32_t Reserved2[2];
    volatile uint32_t AHB1ENR;
    volatile uint32_t AHB2ENR;
    volatile uint32_t Reserved3[2];
    volatile uint32_t APB1ENR;
    volatile uint32_t APB2ENR;
    volatile uint32_t Reserved4[2];
    volatile uint32_t AHB1LPENR;
    volatile uint32_t AHB2LPENR;
    volatile uint32_t Reserved5[2];
    volatile uint32_t APB1LPENR;
    volatile uint32_t APB2LPENR;
    volatile uint32_t Reserved6[2];
    volatile uint32_t BDCR;
    volatile uint32_t CSR;
    volatile uint32_t Reserved7[2];
    volatile uint32_t SSCGR;
    volatile uint32_t PLLI2SCFGR;
    volatile uint32_t Reserved8;
    volatile uint32_t DCKCFGR;
}__attribute__((packed)) RCCRegister_t;


typedef struct GPIORegister{
    volatile uint32_t MODER;
    volatile uint32_t OTYPER;
    volatile uint32_t OSPEEDR;
    volatile uint32_t PUPDR;
    volatile uint32_t IDR;
    volatile uint32_t ODR;
    volatile uint32_t BSRR;
    volatile uint32_t LCKR;
    volatile uint32_t AFRL;
    volatile uint32_t AFRH;
}__attribute__((packed)) GPIORegister_t;

extern RCCRegister_t *rcc_register;
extern GPIORegister_t *gpio_a_register;
extern GPIORegister_t *gpio_b_register;
extern GPIORegister_t *gpio_c_register;
extern GPIORegister_t *gpio_d_register;
extern GPIORegister_t *gpio_e_register;
extern GPIORegister_t *gpio_h_regsiter;

class System {
public:
    enum class ClockChannel{
        /* AHB1 peripheral. */
        GPIOA = 0,
        GPIOB = 1,
        GPIOC = 2,
        GPIOD = 3,
        GPIOE = 4,
        GPIOH = 7,
        CRC = 12,
        DMA1 = 21,
        DMA2 = 22,
        /* AHB2 peripheral. */
        OTGFS = 7 + (32 * 1),
        /* APB1 peripheral. */
        TIM2 = 0 + (32 * 2),
        TIM3 = 1 + (32 * 2),
        TIM4 = 2 + (32 * 2),
        TIM5 = 3 + (32 * 2),
        WWDG = 11 + (32 * 2),
        SPI2 = 14 + (32 * 2),
        SPI3 = 15 + (32 * 2),
        USART2 = 17 + (32 * 2),
        I2C1 = 21 + (32 * 2),
        I2C2 = 22 + (32 * 2),
        I2C3 = 23 + (32 * 2),
        PWR = 28 + (32 * 2),
        /* APB2 peripheral. */
        TIM1 = 0 + (32 * 3),
        USART1 = 4 + (32 * 3),
        USART6 = 5 + (32 * 3),
        ADC1 = 8 + (32 * 3),
        SDIO = 11 + (32 * 3),
        SPI1 = 12 + (32 * 3),
        SPI4 = 13 + (32 * 3),
        SYSCFG = 14 + (32 * 3),
        TIM9 = 16 + (32 * 3),
        TIM10 = 17 + (32 * 3),
        TIM11 = 18 + (32 * 3),
    };

    static bool setClockChannelStatus(ClockChannel channel,bool status);
    static bool getClockChannelStatus(ClockChannel channel);
};

#endif